﻿using System;
using System.Globalization;
using Fwql.Net.Extensions;

namespace Fwql.Net.Utils
{
    /// <summary>
    /// 中国农历日期类
    /// </summary>
   public class ChineseDateTime
    {
       #region 字段
        private readonly ChineseLunisolarCalendar _calendar = new ChineseLunisolarCalendar();

        private static readonly string[] DayArray = new[]
                                                        {
                                                            "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
                                                            "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
                                                            "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
                                                        };

        private static readonly string[] DiZhiArray = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };
        private static readonly string[] MonthArray = new[] { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊" };
        private static readonly string[] ShengXiaoArray = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
        private static readonly string[] TianGanArray = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };

        #endregion

        #region 构造函数

        /// <summary>
        /// 以当前公历日期创建中国农历日期对象
        /// </summary>
        public ChineseDateTime() : this(DateTime.Now) { }

        /// <summary>
        /// 创建中国农历日期对象
        /// </summary>
        /// <param name="dateTime">公历日期</param>
        public ChineseDateTime(DateTime dateTime)
        {
            this.DateTime = dateTime;
            Guard.NotOutOfRange(this.DateTime.Date, "dateTime", _calendar.MinSupportedDateTime, _calendar.MaxSupportedDateTime);
            Refresh();
        }

        /// <summary>
        /// 创建中国农历日期对象
        /// </summary>
        /// <param name="dateTime">农历日期</param>
        /// <param name="isLeapMonth">是否为闰月，默认为false</param>
        public ChineseDateTime(DateTime dateTime, bool isLeapMonth = false)
        {
            DateTime minSupported = new DateTime(1901, 1, 1);
            DateTime maxSupported = new DateTime(2100, 12, 29);

            Guard.NotOutOfRange(dateTime, "dateTime", minSupported, maxSupported);

            int num1 = 0, num2;
            int leapMonth = _calendar.GetLeapMonth(dateTime.Year);

            if (((leapMonth == dateTime.Month + 1) && isLeapMonth) || (leapMonth > 0 && leapMonth <= dateTime.Month))
                num2 = dateTime.Month;
            else
                num2 = dateTime.Month - 1;

            while (num2 > 0)
            {
                num1 += _calendar.GetDaysInMonth(dateTime.Year, num2--);
            }

            this.DateTime = GetChinaNewYearDateTime(dateTime.Year).AddDays(num1 + dateTime.Day - 1);
            Refresh();
        }

        /// <summary>
        /// 创建中国农历日期对象
        /// </summary>
        /// <param name="year">农历年</param>
        /// <param name="month">农历月</param>
        /// <param name="day">农历日</param>
        /// <param name="isLeapMonth">是否为闰月，默认为false</param>
        public ChineseDateTime(int year, int month, int day, bool isLeapMonth = false)
            : this(new DateTime(year, month, day), isLeapMonth) { }

        #endregion

        #region 属性

        /// <summary>
        /// 获得或修改公历日期
        /// </summary>
        public DateTime DateTime { get; private set; }

        /// <summary>
        /// 农历年
        /// </summary>
        public int Year { get; private set; }

        /// <summary>
        /// 农历月
        /// </summary>
        public int Month { get; private set; }

        /// <summary>
        /// 农历日
        /// </summary>
        public int Day { get; private set; }

        /// <summary>
        /// 农历闰月
        /// </summary>
        private int LeapMonth { get; set; }

        /// <summary>
        /// 是否为闰月
        /// </summary>
        public bool IsLeapMonth { get; private set; }

        /// <summary>
        /// 农历年天干
        /// </summary>
        public string Tiangan { get; private set; }

        /// <summary>
        /// 农历年地支
        /// </summary>
        public string Dizhi { get; private set; }

        /// <summary>
        /// 农历年生肖
        /// </summary>
        public string Shengxiao { get; private set; }

        /// <summary>
        /// 农历年文本
        /// </summary>
        public string CnYear { get; private set; }

        /// <summary>
        /// 农历月文本
        /// </summary>
        public string CnMonth { get; private set; }

        /// <summary>
        /// 农历日文本
        /// </summary>
        public string CnDay { get; private set; }

        #endregion

        #region 方法

        /// <summary>
        /// 获取指定年份正月初一的公历日期
        /// </summary>
        /// <param name="year">指定的年份</param>
        public DateTime GetChinaNewYearDateTime(int year)
        {
            var dt = new DateTime(year, 1, 1);
            int cnYear = _calendar.GetYear(dt);
            int cnMonth = _calendar.GetMonth(dt);

            int num1 = 0;
            int num2 = _calendar.IsLeapYear(cnYear) ? 13 : 12;

            while (num2 >= cnMonth)
            {
                num1 += _calendar.GetDaysInMonth(cnYear, num2--);
            }

            num1 = num1 - _calendar.GetDayOfMonth(dt) + 1;
            return dt.AddDays(num1);
        }

        /// <summary>
        /// 刷新对象属性
        /// </summary>
        private void Refresh()
        {
            this.Year = _calendar.GetYear(this.DateTime); //年
            this.Month = _calendar.GetMonth(this.DateTime); //月
            this.LeapMonth = _calendar.GetLeapMonth(this.Year); //闰月
            if (this.LeapMonth == this.Month)
            {
                this.IsLeapMonth = true;
                this.Month--;
            }
            else if (this.LeapMonth > 0 && this.LeapMonth < this.Month)
            {
                this.Month--;
            }
            this.Day = _calendar.GetDayOfMonth(this.DateTime); //日
            int sy = _calendar.GetSexagenaryYear(this.DateTime);
            this.Tiangan = TianGanArray[sy % 10 - 1];
            this.Dizhi = DiZhiArray[sy % 12 - 1];
            this.Shengxiao = ShengXiaoArray[sy % 12 - 1]; //生肖
            this.CnYear = this.Tiangan.Append(this.Dizhi, this.Shengxiao, "年"); //农历年文本
            this.CnMonth = MonthArray[this.Month - 1] + "月"; //农历月文本
            this.CnDay = DayArray[this.Day - 1]; //农历日文本
        }

        #endregion

        /// <summary>
        /// 输入农历日期字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.CnYear.Append(this.CnMonth, this.CnDay);
        }
    }
}
